#pragma once 
#include "win_handle.hpp"
#include "event_router.hpp"
#include "icon.hpp"
#include "cursor.hpp"
#include "menu.hpp"
#include "application.hpp"

namespace winboost {
   namespace windows {


class basic_window : public resources::win_handle,
                     public virtual events::event_router
{
public:
   basic_window(void);
   virtual ~basic_window(void);

   virtual void subclass_window(value_type hwnd);
   virtual HWND handle(void) const { return win_handle::handle(); }
  
   bool create(void);
   void show(int show_cmd = SW_SHOW);
   void close(void);

   bool is_valid(void) const { return (::IsWindow(handle()) == TRUE); }
   resources::icon& icon_lg(void) const { return const_cast<resources::icon&>(m_icon_lg); }
   resources::icon& icon_sm(void) const { return const_cast<resources::icon&>(m_icon_sm); }
   resources::cursor& cursor(void) const { return const_cast<resources::cursor&>(m_cursor); }
   resources::menu& menu(void) const { return const_cast<resources::menu&>(m_menu); }
   u_string get_title() const { return m_title; }
   void set_title(u_string val) { m_title = val; }
   u_string get_window_text(void) const;
   void set_window_text(LPCTSTR val);

protected:
   void hook_window(resources::win_handle* ptr);
   void unhook_window(void);
   void register_class(CREATESTRUCT& cs);

   //-------------------------------------------
   // application specific creation override
   //-------------------------------------------
   virtual bool pre_create_window(CREATESTRUCT& cs){ return true; }
   //-------------------------------------------
   // application specific registration override
   //-------------------------------------------
   virtual void pre_register_class(WNDCLASSEX& wcex){}
   virtual LRESULT default_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);

private:
   resources::icon m_icon_lg;
   resources::icon m_icon_sm;
   resources::cursor m_cursor;
   resources::menu m_menu;
   u_string m_title;
   
   //------------------------------------
   // Trigger function for update ui events
   //------------------------------------
   LRESULT on_initmenupopup(const winboost::events::message& e);
};

//-------------------------------------
// implementation
//-------------------------------------
inline basic_window::basic_window(void)
{
   m_cursor.set_std_cursor(IDC_ARROW);
   set_message_handler(WM_INITMENUPOPUP, boost::bind(&basic_window::on_initmenupopup, this, _1));
}

inline basic_window::~basic_window(void)
{
}

inline bool basic_window::create(void)
{
   CREATESTRUCT cs;
   cs.hInstance   = instance_handle();
   cs.style       = WS_OVERLAPPEDWINDOW;
   cs.dwExStyle   = 0;
   cs.cx          = CW_USEDEFAULT;
   cs.cy          = CW_USEDEFAULT;
   cs.x           = CW_USEDEFAULT;
   cs.y           = CW_USEDEFAULT;
   cs.hMenu       = m_menu.handle();
   cs.hwndParent  = NULL;
   cs.lpszClass   = NULL;
   cs.lpszName    = m_title.c_str();
   cs.lpCreateParams = NULL;

   if(!pre_create_window(cs))
      return false;

   if(cs.lpszClass == NULL)
      register_class(cs);

   hook_window(this);

   HWND hwnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y,
      cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);

   if(hwnd == NULL)
   {
      DWORD err = ::GetLastError();
      throw window_exception("window creation failed");
   }

   unhook_window();
   return true;
}

inline void basic_window::show(int show_cmd /*= SW_SHOW*/)
{
   if(!is_valid())
      create();

   ::ShowWindow(handle(), show_cmd);
   ::UpdateWindow(handle());
}

inline void basic_window::register_class(CREATESTRUCT& cs)
{
   cs.lpszClass = m_class.c_str();

   WNDCLASSEX wcex;
   wcex.cbSize = sizeof(WNDCLASSEX);
   wcex.style			= CS_HREDRAW | CS_VREDRAW;
   wcex.lpfnWndProc	= wnd_proc;
   wcex.cbClsExtra	= 0;
   wcex.cbWndExtra	= 0;
   wcex.hInstance		= cs.hInstance;
   wcex.hIcon			= m_icon_lg.handle();
   wcex.hCursor		= m_cursor.handle();
   wcex.hbrBackground= (HBRUSH)(COLOR_WINDOW+1);
   wcex.lpszMenuName	= NULL;
   wcex.lpszClassName= cs.lpszClass;
   wcex.hIconSm		= m_icon_sm.handle();

   pre_register_class(wcex);

   m_class_atom = ::RegisterClassEx(&wcex);  
}

inline void basic_window::hook_window(resources::win_handle* ptr)
{
   state::state_ptr_type pstate = _the_module.get_thread_state();

   if(pstate->m_pwnd == ptr)
      return;

   if(pstate->m_old_hook == NULL)
      pstate->m_old_hook = ::SetWindowsHookEx(WH_CBT, basic_window::cbt_hook, NULL, ::GetCurrentThreadId());

   assert(ptr->handle() == NULL);
   assert(pstate->m_pwnd == NULL);
   pstate->m_pwnd = ptr;
}

inline void basic_window::unhook_window(void)
{
   state::state_ptr_type pstate = _the_module.get_thread_state();
   if(pstate->m_old_hook != NULL)
   {
      ::UnhookWindowsHookEx(pstate->m_old_hook);
      pstate->m_old_hook = NULL;
   }
   if(pstate->m_pwnd != NULL)
      pstate->m_pwnd = NULL;
}

inline LRESULT basic_window::default_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
   return ::DefWindowProc(hwnd, message, wparam, lparam);
}

inline void basic_window::close(void)
{
	::SendMessage(handle(), WM_CLOSE, 0, 0);
}

//------------------------------------
// Trigger function for update ui events
//------------------------------------
inline LRESULT basic_window::on_initmenupopup(const winboost::events::message& e)
{
   HMENU hmenu = reinterpret_cast<HMENU>(e.wparam());
   int index = e.lo_lparam();

   events::cmd_ui cmdui;

   cmdui.set_menu(hmenu);
   int count = ::GetMenuItemCount(hmenu);

   for (int i = 0; i < count; i++)
   {
      UINT id = ::GetMenuItemID(hmenu, i);
      if(id == 0)
         continue;

      cmdui.set_cmd_id(id);
      cmdui.set_index(i);

      // Check if handler exists and enable
      ::SendMessage(handle(), WM_COMMAND, (WPARAM)&cmdui, (LPARAM)disable_unhandled_commands);

      LRESULT res = ::SendMessage(handle(), WM_COMMAND, (WPARAM)&cmdui, (LPARAM)-1);
      if(res == do_not_route_further)
         cmdui.enable(false);

   }

   return 0;
}

inline void basic_window::subclass_window(value_type hwnd)
{
   pre_subclass(hwnd);

   state::state_ptr_type pstate = _the_module.get_thread_state();

   _the_module.add_window(hwnd, this);
   m_handle = handle_type(hwnd, resources::deleter_window());
   // dialog for now has its own procedure
   if(!is_dialog())
      WNDPROC old_wndproc = (WNDPROC)::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (DWORD_PTR)wnd_proc);
}

inline u_string basic_window::get_window_text(void) const
{
   int len = ::GetWindowTextLength(handle());
   boost::scoped_array<TCHAR> buffer(new TCHAR[len+1]);
   ::GetWindowText(handle(), buffer.get(), len+1);
   return u_string(buffer.get());
}

inline void basic_window::set_window_text(LPCTSTR val)
{
   // window is not yet created or subclassed
   assert(handle());
   ::SetWindowText(handle(), val);
}

   }
}


